package by.dkusch.aircompany.dao.impl;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.TypedQuery;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Join;
import javax.persistence.criteria.JoinType;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.SingularAttribute;

import org.hibernate.jpa.criteria.OrderImpl;
import org.springframework.stereotype.Repository;

import by.dkusch.aircompany.dao.FlightDao;
import by.dkusch.aircompany.model.filter.FilterFlight;
import by.dkusch.aircompany.model.filter.base.FilterEntity;
import by.dkusch.aircompany.model.tables.Crew;
import by.dkusch.aircompany.model.tables.Crew_;
import by.dkusch.aircompany.model.tables.Flight;
import by.dkusch.aircompany.model.tables.Flight_;
import by.dkusch.aircompany.model.tables.Plane;
import by.dkusch.aircompany.model.tables.Plane_;
import by.dkusch.aircompany.model.tables.Route;
import by.dkusch.aircompany.model.tables.Route_;
import by.dkusch.aircompany.model.tables.base.BaseEntity_;

@Repository
public class FlightDaoImpl extends AbstractDaoImpl<Long, Flight> implements
		FlightDao {

	public FlightDaoImpl() {
		super(Flight.class);
	}

	@Override
	public List<Flight> getAllItemsWithFilter(SingularAttribute<? super Flight, ?> attr, boolean ascending, int startRecord, int pageSize, FilterEntity<Long, ? super Flight> filter) {
		if (filter == null) {
			return getAllItems(attr, ascending, startRecord, pageSize);
		}

		CriteriaBuilder cBuilder = getEm().getCriteriaBuilder();
		FilterFlight fc = (FilterFlight) filter;

		CriteriaQuery<Flight> criteria = cBuilder.createQuery(Flight.class);
		Root<Flight> fromFlight = criteria.from(Flight.class);

		@SuppressWarnings("unchecked")
		Join<Flight, Route> fromRoute = (Join<Flight, Route>) fromFlight.fetch(Flight_.route, JoinType.LEFT);
		@SuppressWarnings("unchecked")
		Join<Flight, Plane> fromPlane = (Join<Flight, Plane>) fromFlight.fetch(Flight_.plane, JoinType.LEFT);
		@SuppressWarnings("unchecked")
		Join<Flight, Crew> fromCrew = (Join<Flight, Crew>) fromFlight.fetch(Flight_.crew, JoinType.LEFT);

		OrderImpl order = new OrderImpl(fromFlight.get(BaseEntity_.id), ascending);
		if (attr != null) {
			if (Flight_.route.equals(attr)) {
				order = new OrderImpl(fromRoute.get(Route_.code), ascending);
			} else if (Flight_.plane.equals(attr)) {
				order = new OrderImpl(fromPlane.get(Plane_.code), ascending);
			} else if (Flight_.crew.equals(attr)) {
				order = new OrderImpl(fromCrew.get(Crew_.code), ascending);
			} else {
				order = new OrderImpl(fromFlight.get(attr), ascending);
			}
		}


		List<Predicate> conditions = new ArrayList<Predicate>();

		if (fc.getCrewCode() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromCrew.get(Crew_.code)), fc.getCrewCode().toUpperCase()));
		}

		if (fc.getRouteCode() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromRoute.get(Route_.code)), fc.getRouteCode().toUpperCase()));
		}

		if (fc.getPlaneCode() != null) {
			conditions.add(cBuilder.like(cBuilder.upper(fromPlane.get(Plane_.code)), fc.getPlaneCode().toUpperCase()));
		}

		if ((fc.getDepartureTimeStart() != null) && (fc.getDepartureTimeEnd() != null)) {
			conditions.add(cBuilder.between(fromFlight.get(Flight_.startTime), fc.getDepartureTimeStart(), fc.getDepartureTimeEnd()));
		} else if ((fc.getDepartureTimeStart() != null) && (fc.getDepartureTimeEnd() == null)) {
			conditions.add(cBuilder.greaterThanOrEqualTo(fromFlight.get(Flight_.startTime), fc.getDepartureTimeStart()));
		} else if ((fc.getDepartureTimeStart() == null) && (fc.getDepartureTimeEnd() != null)) {
			conditions.add(cBuilder.lessThanOrEqualTo(fromFlight.get(Flight_.startTime), fc.getDepartureTimeEnd()));
		}

		if ((fc.getArrivalTimeStart() != null) && (fc.getArrivalTimeEnd() != null)) {
			conditions.add(cBuilder.between(fromFlight.get(Flight_.endTime), fc.getArrivalTimeStart(), fc.getArrivalTimeEnd()));
		} else if ((fc.getArrivalTimeStart() != null) && (fc.getArrivalTimeEnd() == null)) {
			conditions.add(cBuilder.greaterThanOrEqualTo(fromFlight.get(Flight_.endTime), fc.getArrivalTimeStart()));
		} else if ((fc.getArrivalTimeStart() == null) && (fc.getArrivalTimeEnd() != null)) {
			conditions.add(cBuilder.lessThanOrEqualTo(fromFlight.get(Flight_.endTime), fc.getArrivalTimeEnd()));
		}

		TypedQuery<Flight> query = getEm().createQuery(criteria.select(fromFlight).where(cBuilder.and(conditions.toArray(new Predicate[] {}))).orderBy(order));

		if (startRecord >= 0) {
			query.setFirstResult(startRecord);
			query.setMaxResults(pageSize);
		}

		List<Flight> results = query.getResultList();
		return results;

	}

}
